package net.cloudcodex.client.character.detail.presenter;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.cloudcodex.client.CloudCodexEventBus;
import net.cloudcodex.client.character.detail.view.CharacterDetailView;
import net.cloudcodex.client.character.detail.view.CharacterDetailViewImpl;
import net.cloudcodex.client.util.rpc.SuccessCallback;
import net.cloudcodex.shared.dto.campaign.character.CharacterDescriptionDTO;
import net.cloudcodex.shared.dto.campaign.character.CharacterHeaderDTO;
import net.cloudcodex.shared.dto.campaign.character.CharacterNoteDTO;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

/**
 * 
 * 
 * @author ADaroussin
 */
@Presenter(view = CharacterDetailViewImpl.class, multiple = true)
public class CharacterDetailPresenter extends BasePresenter<CharacterDetailView, CloudCodexEventBus> {

	private long campaignId;
	private long characterId;
	private Long byCharacterId;
	private CharacterHeaderDTO characterHeader;
	private CharacterDescriptionDTO characterDescription;

	private final SuccessCallback<CharacterDescriptionDTO> cdCallback = new SuccessCallback<CharacterDescriptionDTO>() {
		public void onSuccess(CharacterDescriptionDTO result) {
			characterDescription = result;

			updateData();
		};
	};

	@Override
	public void bind() {
		view.addValueChangeHandler(new ValueChangeHandler<Object>() {
			@Override
			public void onValueChange(ValueChangeEvent<Object> event) {
				view.setUpdateButtonEnabled(isDataChanged());
			}
		});
		view.getUpdateButton().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {

				final String publicDescription = view.getPublicDescription();
				final String privateDescription = view.getPrivateDescription();
				final Boolean dead = view.isDead();
				final Boolean locked = view.isLocked();

				characterHeader.setDescription(publicDescription);
				characterDescription.setSheet(privateDescription);
				characterHeader.setDead(dead);
				characterHeader.setLocked(locked);

				final List<CharacterNoteDTO> notes = characterDescription.getNotes();
				if (notes != null) {
					for (CharacterNoteDTO note : notes) {
						note.setContent(view.getNote(note.getAuthor()));
					}
				}

				final Map<Long, String> aliases = characterDescription.getAliases();
				if (aliases != null) {
					for (Entry<Long, String> entry : aliases.entrySet()) {
						final String alias = view.getAlias(entry.getKey());
						entry.setValue(alias);
					}
				}

				// Update character description but don't refresh the view
				eventBus.updateCharacterDescription(campaignId, characterId, byCharacterId, characterHeader,
						characterDescription);
			}
		});
		view.getCancelButton().addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				// Overwrite view changes with original data
				updateData();
			}
		});
	}

	public void initCharacterDetail(Long byCharacterId, CharacterHeaderDTO characterHeader,
			CharacterDescriptionDTO characterDescription) {
		this.byCharacterId = byCharacterId;
		this.characterHeader = characterHeader;
		this.characterDescription = characterDescription;
		this.campaignId = characterDescription.getCampaignId();
		this.characterId = characterHeader.getId();

		updateData();
	}

	public void initCharacterDetail(long campaignId, long characterId, Long byCharacterId,
			CharacterHeaderDTO characterHeader) {
		this.campaignId = campaignId;
		this.characterId = characterId;
		this.byCharacterId = byCharacterId;
		this.characterHeader = characterHeader;

		eventBus.getCharacterDescription(campaignId, characterId, byCharacterId, cdCallback);
	}

	protected void updateData() {
		final boolean isGameMaster = isGameMaster();
		view.setCharacter(characterHeader.getName(), characterHeader.getIcon());
		view.displayAdvancedInformation(isGameMaster);
		view.setDead(characterHeader.getDead());
		view.setLocked(characterHeader.getLocked());
		view.setPublicDescription(characterHeader.getDescription(), isGameMaster);
		view.setPrivateDescription(characterDescription.getSheet(), isGameMaster);

		// TODO: Iterate over ALL campaign's character to allow new notes
		final List<CharacterNoteDTO> notes = characterDescription.getNotes();
		if (notes != null) {
			for (CharacterNoteDTO note : notes) {
				final Long author = note.getAuthor();
				if (isOwnCharacter(author)) {
					view.setPersonnalNote(note.getContent());
				} else {
					view.setNote(author, note.toString(), null, note.getContent(), isGameMaster());
				}
			}
		}
		// TODO: Iterate over ALL campaign's character to allow new aliases
		final Map<Long, String> aliases = characterDescription.getAliases();
		if (aliases != null) {
			for (Long characterId : aliases.keySet()) {
				final String alias = aliases.get(characterId);
				view.setAlias(characterId, alias, alias);
			}
		}
		view.setUpdateButtonEnabled(false);
	}

	protected boolean isDataChanged() {
		if (!equals(view.getPublicDescription(), characterHeader.getDescription())) {
			return true;
		}
		if (!equals(view.getPrivateDescription(), characterDescription.getSheet())) {
			return true;
		}
		if (!equals(view.isDead(), characterHeader.getDead())) {
			return true;
		}
		if (!equals(view.isLocked(), characterHeader.getLocked())) {
			return true;
		}
		// TODO: Iterate over ALL campaign's character to detect new notes
		final List<CharacterNoteDTO> notes = characterDescription.getNotes();
		if (notes != null) {
			for (CharacterNoteDTO note : notes) {
				final Long author = note.getAuthor();
				if (isOwnCharacter(author)) {
					if (!equals(view.getNote(author), note.getContent())) {
						return true;
					}
				} else {
					if (!equals(view.getNote(author), note.getContent())) {
						return true;
					}
				}
			}
		}
		// TODO: Iterate over ALL campaign's character to detect new aliases
		final Map<Long, String> aliases = characterDescription.getAliases();
		if (aliases != null) {
			for (Entry<Long, String> entry : aliases.entrySet()) {
				if (!equals(view.getAlias(entry.getKey()), entry.getValue())) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean isGameMaster() {
		return byCharacterId == null;
	}

	private boolean isOwnCharacter(Long characterId) {
		return byCharacterId.equals(characterId);
	}

	private boolean equals(Object o1, Object o2) {
		return o1 == null ? o2 == null : o1.equals(o2);
	}

}
